package com.zaoniao.enstudy.domain.service;

import com.alibaba.excel.util.ListUtils;
import com.zaoniao.enstudy.domain.entity.ExamAnswer;
import com.zaoniao.enstudy.domain.entity.ExamPaper;
import com.zaoniao.enstudy.domain.entity.ExamWords;
import com.zaoniao.enstudy.domain.entity.ZaoWord;
import com.zaoniao.enstudy.domain.entity.bo.ExamPaperBo;
import com.zaoniao.enstudy.domain.repository.ExamRepository;
import com.zaoniao.enstudy.domain.repository.ZaoWordRepository;
import com.zaoniao.enstudy.infrastructure.po.ExamWordsPo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ExamService {

    /**
     * 答题选项集合。
     */
    private final static List<String> options = Arrays.asList("a", "b", "c", "d");
    /**
     * 每隔100条存储数据库，然后清理list ，方便内存回收
     */
    private static final int BATCH_COUNT = 100;
    /**
     * 缓存的数据
     */
    private List<ExamWordsPo> examWordsPos = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

    private final ZaoWordRepository zaoWordRepository;

    private final ExamRepository examRepository;

    /**
     * 执行单词测试题生成.
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void allWordsPager() {
        // 1. 获取后端单词表的所有数据.
        List<ZaoWord> allWords = zaoWordRepository.getAll();
        // 2. 把单词的翻译放入一个集合中.
        List<String> translations = allWords.stream().map(ZaoWord::getTranslation).collect(Collectors.toList());
        // 3. 清空单词测试表.
        examRepository.deleteAll();
        // 每100条数据批量处理一次.
        for (ZaoWord word : allWords) {
            // 把单词正确选项放入 a, b, c, d 中的一个.其他的选项从别的单词中获取.
            ExamWordsPo examWordsPo = new ExamWordsPo();
            examWordsPo.setWord(word.getWord());
            examWordsPo.setCreateTime(new Date());
            examWordsPo.setCreateUser("admin");
            examWordsPo.setUpdateTime(new Date());
            examWordsPo.setUpdateUser("admin");
            examWordsPo.setStatus("1");
            Random random = new Random();
            int randomIndex = random.nextInt(options.size());
            String randomElement = options.get(randomIndex);
            // 构建单词选项.
            this.buildOptions(randomElement, word, examWordsPo, translations);
            examWordsPos.add(examWordsPo);
            if (examWordsPos.size() >= BATCH_COUNT) {
                // 批量插入单词测试数据.
                examRepository.insertList(examWordsPos);
                // 清空缓存数据.
                examWordsPos = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
            }
        }
        // 最后一次批量插入.
        if (CollectionUtils.isNotEmpty(examWordsPos)) {
            // 批量插入单词测试数据.
            examRepository.insertList(examWordsPos);
            // 清空缓存数据.
            examWordsPos.clear();
        }
    }

    /**
     * 构建选项.
     *
     * @param randomElement 随机元素.
     * @param word          单词.
     * @param examWordsPo   单词测试数据.
     * @param translations  翻译.
     */
    private void buildOptions(String randomElement, ZaoWord word, ExamWordsPo examWordsPo, List<String> translations) {
        switch (randomElement) {
            case "a":
                examWordsPo.setOptionA(word.getTranslation());
                examWordsPo.setTrueResult("A");
                break;
            case "b":
                examWordsPo.setOptionB(word.getTranslation());
                examWordsPo.setTrueResult("B");
                break;
            case "c":
                examWordsPo.setOptionC(word.getTranslation());
                examWordsPo.setTrueResult("C");
                break;
            case "d":
                examWordsPo.setOptionD(word.getTranslation());
                examWordsPo.setTrueResult("D");
                break;
        }
        // 随机取出 3 个单词作为选项.
        List<String> randomElements = getRandomElements(translations, 3, word.getTranslation());
        if (CollectionUtils.isNotEmpty(randomElements)) {
            for (String randomElement1 : randomElements) {
                if (StringUtils.isBlank(examWordsPo.getOptionA())) {
                    examWordsPo.setOptionA(randomElement1);
                } else if (StringUtils.isBlank(examWordsPo.getOptionB())) {
                    examWordsPo.setOptionB(randomElement1);
                } else if (StringUtils.isBlank(examWordsPo.getOptionC())) {
                    examWordsPo.setOptionC(randomElement1);
                } else if (StringUtils.isBlank(examWordsPo.getOptionD())) {
                    examWordsPo.setOptionD(randomElement1);
                }
            }
        }
    }

    /**
     * 获取随机元素.
     *
     * @param list        列表
     * @param numElements 获取元素的个数
     * @return 随机元素
     */
    public <T> List<T> getRandomElements(List<T> list, int numElements, T excludeElement) {
        List<T> allElements = new ArrayList<>(list);
        List<T> result = new ArrayList<>();
        // 排除指定元素
        allElements.removeIf(element -> element.equals(excludeElement));
        Random random = new Random();
        int listSize = allElements.size();
        for (int i = 0; i < numElements; i++) {
            if (listSize == 0) {
                log.error("异常数据");
            }
            int randomIndex = random.nextInt(listSize);
            result.add(allElements.remove(randomIndex));
            listSize--;
        }
        return result;
    }

    /**
     * 生成考试试卷.
     *
     * @param examCount 考题数.
     * @param userId    用户id.
     * @return 考题数据.
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ExamPaperBo examGenerate(Integer examCount, String userId) {
        // 1. 从单词测试表中随机取出 {examCount} 条数据.且不在用户考试记录中.
        List<ExamWords> examWordsPos = examRepository.randomSelect(examCount, userId);
        // 1.1 List 转换成栈.
        Stack<ExamWords> stack = new Stack<>();
        examWordsPos.forEach(stack::push);

        ExamPaper examPaper = new ExamPaper();
        examPaper.setUserId(userId);
        examPaper.setQuestionCount(examCount);
        examPaper.setTrueCount(0);
        examPaper = examRepository.insertExamPaper(examPaper);
        // 2. 封装10个考题入库 z_exam_answer.
        List<ExamAnswer> examAnswers = new ArrayList<>();
        for (int i = 1; i <= examCount; i++) {
            // 2.1 从栈中取出一个单词.
            ExamWords examWord = stack.pop();
            // 2.2 封装 z_exam_answer 数据.
            ExamAnswer answer = new ExamAnswer();
            answer.setWord(examWord.getWord());
            answer.setExamPaperId(examPaper.getId());
            answer.setOrder(i);
            // 用户答题选项初始化为空.
            answer.setUserAnswer("");
            examAnswers.add(answer);
        }
        examRepository.insertBatchExamAnswer(examAnswers);
        // 3. 封装返回数据.
        ExamPaperBo examPaperBo = new ExamPaperBo();
        examPaperBo.setExamPaper(examPaper);
        examPaperBo.setExamAnswerList(examAnswers);
        return examPaperBo;
    }

    /**
     * 开始考试.
     *
     * @param paperId 试卷id.
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void examStart(Integer paperId) {
        examRepository.startExamPaper(paperId);
    }

    /**
     * 结束考试.
     *
     * @param paperId 试卷id.
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void examEnd(Integer paperId) {
        examRepository.endExamPaper(paperId);
    }

    /**
     * 用户考试记录列表.
     *
     * @param userId 用户Id
     * @return 个人考试记录列表
     */
    @Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
    public List<ExamPaper> examList(String userId) {
        return examRepository.examList(userId);
    }

    /**
     * 用户答题.
     * @param examAnswer 答题数据
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void answerSubmit(ExamAnswer examAnswer) {
        // 1 更新答题明细答案。
        examRepository.updateAnswer(examAnswer);
        // 2 计算用户正确数。
        ExamWords words = examRepository.getExamWord(examAnswer.getWord());
        // 2.1 答案正确，更新用户答题正确数.
        if(StringUtils.equals(examAnswer.getUserAnswer(), words.getTrueResult())){
            examRepository.updatePaperTrueCount(examAnswer.getExamPaperId());
        }
    }
}
